package com.tsd.common.controller;

import com.alibaba.druid.pool.GetConnectionTimeoutException;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.tsd.CommonMsgNameConstants;
import com.tsd.CommonParamNameConstants;
import com.tsd.core.config.Config;
import com.tsd.core.constants.ConfigKeyConstants;
import com.tsd.core.constants.Constants;
import com.tsd.core.datasource.DataSourceContextHolder;
import com.tsd.core.jwt.AccessToken;
import com.tsd.core.jwt.JwtUtils;
import com.tsd.core.utils.*;
import com.tsd.core.vo.*;
import com.tsd.system.entity.SysUserExt;
import com.tsd.system.utils.SysUtil;
import io.jsonwebtoken.Claims;
import org.apache.ibatis.exceptions.PersistenceException;
import org.apache.shiro.authc.AuthenticationException;
import org.mybatis.spring.MyBatisSystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 控制类的基类
 * 提供公共处理方法
 *
 * @author wudw
 */
public class BaseController {
    static String encodingAESKey;

    static {
        encodingAESKey = Config.getParameterValue(ConfigKeyConstants.ENCODING_AES_KEY);
    }

    private static String picResourceWebApp;
    private static String debug = null;
    @Autowired
    protected HttpServletRequest request;

    /**
     * 未处理的异常日志处理输出器
     */
    private static final Logger NONE_DONE_EXCEPTION_LOGGER = LoggerFactory.getLogger(Constants.NONE_DONE_EXCEPTION_LOGGER);

    public static String getPicResourceWebApp() {
        if (HlpUtils.isEmpty(picResourceWebApp)) {
            picResourceWebApp = Config.getParameterValue(ConfigKeyConstants.PIC_RESOURCE_WEB_APP);
        }
        return picResourceWebApp;
    }

    protected void procException(HttpResult<?> hr, Exception e) {
        if (debug == null) {
            debug = Config.getParameterValue(ConfigKeyConstants.DEBUG);
        }
        procException(hr, e, debug);
    }

    protected void procException(HttpResult<?> hr, Exception e, String debug) {
        if (e instanceof HlpException4Login) {
            if (hr != null) {
                hr.setCode(ErrorCode.CODE_NO_LOGINNED);
                hr.setMessage(e.getMessage());
            }
        } else if (e instanceof HlpException || e instanceof AuthenticationException) {
            if (hr != null) {
                hr.setCode(ErrorCode.CODE_PROC_ERROR);
                hr.setMessage(e.getMessage());
            }
        } else if (e instanceof org.springframework.jdbc.BadSqlGrammarException || e instanceof java.sql.SQLSyntaxErrorException ||
                e instanceof org.springframework.jdbc.IncorrectResultSetColumnCountException ||
                e instanceof org.springframework.jdbc.InvalidResultSetAccessException ||
                e instanceof org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException ||
                e instanceof org.springframework.jdbc.UncategorizedSQLException || e instanceof MyBatisSystemException) {
            Throwable t = ExceptionUtil.getRootThrowable(e);
            if (hr != null) {
                hr.setCode(ErrorCode.CODE_PROC_ERROR);
                hr.setMessage(CommonMsgNameConstants.ERROR_MSG_DATABASE + t.getMessage());
                String trace = ExceptionUtil.getStackTraceFromException(e);
                hr.setTrace(trace);
            }
        } else if (e instanceof org.apache.ibatis.binding.BindingException) {
            hr.setCode(ErrorCode.CODE_PROC_ERROR);
            hr.setMessage("数据绑定错误");
            String trace = ExceptionUtil.getStackTraceFromException(e);
            hr.setTrace(trace);
        } else if (e instanceof org.springframework.jdbc.CannotGetJdbcConnectionException
                || e instanceof GetConnectionTimeoutException ||
                e instanceof PersistenceException) {
            hr.setCode(ErrorCode.CODE_PROC_ERROR);
            hr.setMessage(CommonMsgNameConstants.ERROR_MSG_DATABASE_CONNECT);
            String trace = ExceptionUtil.getStackTraceFromException(e);
            hr.setTrace(trace);
        } else if (e instanceof com.itextpdf.text.exceptions.InvalidPdfException) {
            hr.setCode(ErrorCode.CODE_PROC_ERROR);
            hr.setMessage(CommonMsgNameConstants.ERROR_MSG_PDF_FILE);
            String trace = ExceptionUtil.getStackTraceFromException(e);
            hr.setTrace(trace);
        } else if (e instanceof org.apache.shiro.session.UnknownSessionException) {
            hr.setCode(ErrorCode.CODE_PROC_ERROR);
            hr.setMessage(CommonMsgNameConstants.ERROR_MSG_SESSION_EXPIRED);
            String trace = ExceptionUtil.getStackTraceFromException(e);
            hr.setTrace(trace);
        } else if (e instanceof org.springframework.web.HttpRequestMethodNotSupportedException) {
            hr.setCode(ErrorCode.CODE_PROC_ERROR);
            hr.setMessage(CommonMsgNameConstants.ERROR_MSG_REQ_METHOD_NOT_SUPPORT + e.getMessage());
            String trace = ExceptionUtil.getStackTraceFromException(e);
            hr.setTrace(trace);
        } else if (e instanceof org.springframework.beans.factory.NoSuchBeanDefinitionException ||
                e instanceof java.lang.NoSuchMethodException) {
            hr.setCode(ErrorCode.CODE_PROC_ERROR);
            hr.setMessage(CommonMsgNameConstants.ERROR_MSG_UNDEFINED + e.getMessage());
            String trace = ExceptionUtil.getStackTraceFromException(e);
            hr.setTrace(trace);
        } else {
            String trace = ExceptionUtil.getStackTraceFromException(e);
            String s = ExceptionUtil.getExceptionDesc(e);
            hr.setMessage(s);
            NONE_DONE_EXCEPTION_LOGGER.error(trace);
            hr.setCode(ErrorCode.CODE_INNER_ERROR);
            hr.setMessage(s);
            hr.setTrace(trace);
        }
    }

    /**
     * 获取参数，支持form-data，x-www-form-urlencoded，raw三种方式。x-www-form-urlencoded，raw支持json格式、拼接格式
     *
     * @param request
     * @return
     * @throws UnsupportedEncodingException
     */
    @SuppressWarnings("unchecked")
    protected Map<String, Object> getParams(HttpServletRequest request) throws UnsupportedEncodingException, HlpException {
        Map<String, Object> result = (Map<String, Object>) request.getAttribute(Constants.REQ_ATTRIBUTE_NAME_PARAMS);
        if (result != null) {
            return result;
        }
        return RequestUtils.getParams(request);
    }

    @SuppressWarnings("unchecked")
    protected Map<String, Object> getDataPermissionParams() {
        Map<String, Object> result = (Map<String, Object>) request.getAttribute(Constants.REQ_ATTRIBUTE_NAME_PERMISSION_PARAMS);
        if (result == null) {
            result = new HashMap<>(8);
        }
        return result;
    }

    /**
     * 根据map参数对params内的部门权限进行过滤
     *
     * @param map
     * @param params
     */
    @SuppressWarnings("unchecked")
    protected void filterGroups(Map<String, Object> map, Map<String, Object> params) {
        if (map == null || map.isEmpty() || params == null || params.isEmpty()) {
            return;
        }
        List<String> groupList = HlpUtils.getStringList(map, Constants.PARAMS_KEY_NAME_GROUP_LIST);
        if (!HlpUtils.isEmptyList(groupList)) {
            Object obj = params.get(Constants.PARAMS_KEY_NAME_GROUP_PERMISSION);
            if (obj instanceof List) {
                //如果参数中有部门列表，并且部门权限中包含才算有效，如果过滤的为空还是返回原来的部门
                List<String> permissionList = (List<String>) obj;
                List<String> newList = ListUtil.filter(permissionList, groupList::contains);
                if (!HlpUtils.isEmptyList(newList)) {
                    params.put(Constants.PARAMS_KEY_NAME_GROUP_PERMISSION, newList);
                }
            }
        }
    }

    protected <T> T getParams(HttpServletRequest request, Class<T> clazz) throws UnsupportedEncodingException, HlpException {
        Map<String, Object> map = getParams(request);
        String json = JSON.toJSONString(map);
        return JSON.parseObject(json, clazz);
    }

    /**
     * 检查某个值是否为空（字符串包括null以及"",其他则为null)，如果为空，则提示tip
     *
     * @param value
     * @param tip
     * @throws HlpException
     */
    public void checkEmpty(Object value, String tip) throws HlpException {
        if (HlpUtils.isEmpty(value)) {
            throw new HlpException(tip);
        }
    }

    /**
     * 检查某个list是否为空，如果为空，则提示tip
     *
     * @param list
     * @param tip
     * @throws HlpException
     */
    public void checkEmptyList(List<?> list, String tip) throws HlpException {
        if (HlpUtils.isEmptyList(list)) {
            throw new HlpException(tip);
        }
    }

    /**
     * 检查某个值是否不为空（字符串不为null以及不是"",其他则不是null)，如果不为null，则提示
     *
     * @param value
     * @param tip
     * @throws HlpException
     */
    public void checkNotEmpty(Object value, String tip) throws HlpException {
        if (!HlpUtils.isEmpty(value)) {
            throw new HlpException(tip);
        }
    }

    /**
     * 将一个对象转换为指定类的对象
     *
     * @param clazz
     * @return
     */
    public <T> T getReqParams(Class<T> clazz) {
        Object result = request.getAttribute(Constants.REQ_ATTRIBUTE_NAME_PARAMS);
        if (result != null) {
            return JSON.parseObject(JSON.toJSONString(result), clazz);
        }
        return null;
    }

    protected void copyParams(Map<String, Object> map, Map<String, Object> params, String key, boolean ifLike) {
        if (map.containsKey(key)) {
            if (ifLike) {
                String value = HlpUtils.getString(map, key);
                if (value == null) {
                    value = Constants.VALUE_PERCENTAGE;
                } else {
                    value = Constants.VALUE_PERCENTAGE + value + Constants.VALUE_PERCENTAGE;
                }
                params.put(key, value);
            } else {
                params.put(key, map.get(key));
            }
        }
    }

    /**
     * 从claims获取SysUser信息
     *
     * @return
     * @throws HlpException
     */
    public SysUserExt getSysUser() throws HlpException {
        Claims claims = (Claims) request.getAttribute(CommonParamNameConstants.CLAIMS);
        String token = (String) request.getAttribute(Constants.REQ_HEADER_NAME_TOKEN);
        return getSysUserFromClaims(claims, token);
    }

    /**
     * 从request的token得到SysUserExt
     *
     * @param request
     * @return
     * @throws HlpException
     */
    public SysUserExt getSysUserFromToken(HttpServletRequest request) throws HlpException {
        String token = request.getHeader(Constants.REQ_HEADER_NAME_TOKEN);
        if (HlpUtils.isEmpty(token)) {
            token = request.getParameter(Constants.REQ_HEADER_NAME_TOKEN);
        }
        Claims claims = JwtUtils.parseJWT(token, encodingAESKey);
        return getSysUserFromClaims(claims, token);
    }

    /**
     * 从Claims获取SysUserExt
     *
     * @param claims
     * @param token
     * @return
     * @throws HlpException
     */
    private SysUserExt getSysUserFromClaims(Claims claims, String token) throws HlpException {
        SysUserExt userExt = SysUtil.getSysUserFromClaims(claims);
        if (!HlpUtils.isEmpty(token)) {
            AccessToken accessToken = new AccessToken();
            accessToken.setToken(token);
            userExt.setAccessToken(accessToken);
        }
        return userExt;
    }

    /**
     * 获取json格式参数字符串
     *
     * @param request
     * @return
     * @throws HlpException
     */
    protected String getJSONParamString(HttpServletRequest request) throws HlpException {
        String contentType = request.getContentType();
        if (contentType != null && contentType.toLowerCase().contains(Constants.CONTENT_TYPE_VALUE_APPLICATION_JSON)) {
            return RequestUtils.getInputStreamString(request);
        } else {
            throw new HlpException(CommonMsgNameConstants.ERROR_MSG_CONTENT_TYPE_NOT_JSON);
        }
    }

    protected void procException2Resp(HttpServletResponse response, Exception e) {
        try {
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            response.setHeader(CommonParamNameConstants.CONTENT_TYPE, Constants.CONTENT_TYPE_VALUE_JSON);
            PrintWriter writer = response.getWriter();
            writer.write(e.getMessage());
            writer.flush();
        } catch (Exception e1) {
            NONE_DONE_EXCEPTION_LOGGER.error(ExceptionUtil.getStackTraceFromException(e1));
        }
    }

    /**
     * 从header或者参数中获取account_set_sid，并进行切换账套
     */
    protected void setAccountSetFromParams() {
        String account_set_sid = HttpUtil.getAccountSet(request);
        if (!HlpUtils.isEmpty(account_set_sid)) {
            DataSourceContextHolder.setDataSourceType(account_set_sid.trim());
        } else {
            DataSourceContextHolder.setDataSourceType(null);
        }
    }

    public <T> List<T> getParamsList(String key, Class<T> clazz, boolean must) throws Exception {
        Map<String, Object> map = this.getParams(request);
        return this.getParamsList(map, key, clazz, must);
    }

    public <T> List<T> getParamsList(Map<String, Object> map, String key, Class<T> clazz, boolean must) throws Exception {
        Object obj = map.get(key);
        if (must) {
            this.checkEmpty(obj, CommonMsgNameConstants.getEmptyMsg(key));
        }
        if (obj == null) {
            return null;
        }
        if (!(obj instanceof List)) {
            throw new HlpException(CommonMsgNameConstants.getErrorMsgList(key));
        }
        List<T> list = JSONArray.parseArray(JSON.toJSONString(obj), clazz);
        if (must && HlpUtils.isEmptyList(list)) {
            throw new HlpException(CommonMsgNameConstants.getEmptyMsg2(key));
        }
        return list;
    }

    static String LocalHost = "localhost";
    static String LocalHost_127 = "127.0.0.1";

    private boolean isLocal(String ip) {
        return LocalHost_127.equals(ip) || LocalHost.equalsIgnoreCase(ip);
    }

    /**
     * 获取网络请求的属性数据
     *
     * @param request
     * @return
     */
    public RequestAttribute getRequestAttribute(HttpServletRequest request) {
        RequestAttribute attribute = new RequestAttribute();
        String ip = request.getRemoteAddr();
        if (isLocal(ip)) {
            ip = HttpUtil.getClientIP(request);
        }
        attribute.setClient_ip(ip);
        String protocol = request.getProtocol();
        attribute.setProtocol(protocol);
        attribute.setCookie(request.getHeader(CommonParamNameConstants.COOKIE));
        attribute.setHost(request.getHeader(CommonParamNameConstants.HOST));
        attribute.setReferer(request.getHeader(CommonParamNameConstants.REFERER));
        attribute.setUri(request.getRequestURI());
        attribute.setUser_agent(request.getHeader(CommonParamNameConstants.USER_AGENT));
        return attribute;
    }

}
